import scipy as sp
import utility.utility as UTL
import random

class Chromosome(object):
	
	def __init__(self, D, K):
		'''
		Inizializza un cromosoma che rappresenta una soluzione al problema di clustering scelto:
		 - D e' la dimensionalita' del problema
		 - K e' il numero di cluster da individuare
		'''
		### Imposto il numero di centroidi e di dimensioni
		self.D = D
		self.K = K
		
		### Creo una lista di K elementi, inizialmente contenenti ciascuno una lista vuota
		### L'i-esimo elemento di questa lista rappresenta la lista degli indici dei pattern appartenenti all'i-esimo cluster
		### (cioe' assegnati all'i-esimo centroide)
		p2c = range(K)
		for i in p2c:
			p2c[i] = []
		
		self.p2c = p2c	
		
		### Inizializza la lista degli alleli, in modo da non causare problemi con l'assegnamento
		self._alleles = range(0, D*K)
		
	def p2c_reset(self):
		'''
		Azzera le associazioni punti-cluster 
		'''
		p2c = range(self.K)
		for i in p2c:
			p2c[i] = []
			
		self.p2c = p2c
		
	def _getBoundaries(self, i):
		'''
		metodo ausiliario che restituisce una coppia (b, e)
		i cui membri identificano rispettivamente l'indice iniziale e finale
		delle componenti dell'i-esimo vettore. Gli estremi sono inclusi
		Ad esempio per vettori tridimensionali il terzo vettore e' descritto dalle componenti
		dalla 6 alla 8
		'''
		### TODO includere un controllo per assicurarsi di non cercare un vettore che non esiste
		b = i * self.D
		e = b + self.D - 1
		return (b, e)
		
	def setCentroid(self, i, c):
		'''
		Imposta l'i-esimo centroide
		'''
		b, e = self._getBoundaries(i)
		k = b
		for j in range(0, self.D):
			self._alleles[k] = c[j]
			k = k + 1

	def getCentroid(self, i):
		'''
		Restituisce l'i-esimo centroide
		'''
		b, e = self._getBoundaries(i)
		e = e + 1
		c = sp.array(self._alleles[b:e])
		return c
		
	def getCentroids(self):
		'''
		Restitituisce la lista dei centroidi
		'''
		cs = []
		for i in range(self.K):
			cs.append(self.getCentroid(i))
			
		return cs
		
	def mutate(self, MinMetric, MaxMetric, dataset_min, dataset_max):
		'''
		Effettua la mutazione sul cromosoma
		TODO da testare!!!
		'''
		if (MinMetric == MaxMetric):
			R = 1
		else:
			R = (self.getMetric() - MinMetric)/(MaxMetric - MinMetric)
			
		delta = (random.random() - 0.5) * 2 * R
		
		'''
		print "prima della mutazione:"
		print "delta = ", delta
		print "centroidi: ", self.getCentroids()
		'''
		
		for k in range(self.getNCentroids()):
			c = self.getCentroid(k)
			for i in range(self.getNDimensions()):
				if (delta >= 0):
					c[i] += delta * (dataset_max[i] - c[i]) 
				else:
					c[i] += delta * (c[i] - dataset_min[i])
			
			self.setCentroid(k, c)
		
		'''
		print "dopo la mutazione:"
		print "centroidi: ", self.getCentroids()
		print "-----------------------"
		'''
		
	def computeMetric(self, Patterns):
		'''
		Calcola il valore di metric per il cromosoma corrente
		e lo restituisce come risultato
		'''
		M = 0
		
		_centroids = self.getCentroids()
		
		for k in range(self.K):
			for j in self.p2c[k]:
				M = M + UTL.norma(Patterns[j], _centroids[k])
		
		self._Metric = M
		return M
		
	def getMetric(self):
		'''
		Si limita a restituire il valore di Metric calcolato in precedenza
		e salvato come una proprieta' privata dell'oggetto
		'''
		return self._Metric
		
	def getFitness(self):
		'''
		Nell'algoritmo KGA la fitness viene definita come il reciproco della Metrica
		'''
		return 1/self.getMetric()
		
	def myPrint(self):
		for k in range(self.getNCentroids()):
			print self.getCentroid(k)
		
		print ""	
			
		#print self._alleles
		
	def getNCentroids(self):
		return self.K
		
	def getNDimensions(self):
		return self.D
